////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Filename:    FootballFrenzyGuts.cpp										  //
//  Developer:																  //	
//  Create date:                                                              //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Description: This is the class implementation for the slot gut of the     //
//               Australian Slot machine called Football Frenzy.			  //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//        2010 - Riplife Gaming.  All rights reserved.						  //
////////////////////////////////////////////////////////////////////////////////

#include "FootballFrenzyGuts.h"
#include "slotconn.h"
#include "sltsql.h"

////////////////////////////////////////////////////////////////////////////
/// FruitFrenzyGuts::m_aLineWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Pears, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_ACE ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_ACE.
////////////////////////////////////////////////////////////////////////////
FootballFrenzyGuts::SpinResult FootballFrenzyGuts::m_aLineWinSpinResults[][6] = 
{
// Number of icons:			0			1			2					3						4						5
/* ICON_WILD */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_PIC1 */			{	EVT_LOSER,	EVT_LOSER,	EVT_TWO_PIC1,		EVT_THREE_PIC1,			EVT_FOUR_PIC1,			EVT_FIVE_PIC1		},
/* ICON_PIC2 */			{	EVT_LOSER,	EVT_LOSER,	EVT_TWO_PIC2,		EVT_THREE_PIC2,			EVT_FOUR_PIC2,			EVT_FIVE_PIC2		},
/* ICON_PIC3 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_PIC3,			EVT_FOUR_PIC3,			EVT_FIVE_PIC3		},
/* ICON_PIC4 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_PIC4,			EVT_FOUR_PIC4,			EVT_FIVE_PIC4		},
/* ICON_ACE */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_ACE,			EVT_FOUR_ACE,			EVT_FIVE_ACE		},
/* ICON_KING */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_KING,			EVT_FOUR_KING,			EVT_FIVE_KING		},
/* ICON_QUEEN */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_QUEEN,		EVT_FOUR_QUEEN,			EVT_FIVE_QUEEN		},
/* ICON_JACK */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_JACK,			EVT_FOUR_JACK,			EVT_FIVE_JACK		},
/* ICON_TEN */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_TEN,			EVT_FOUR_TEN,			EVT_FIVE_TEN		},
/* ICON_NINE */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_NINE,			EVT_FOUR_NINE,			EVT_FIVE_NINE		},
/* ICON_SCATTER */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_FREE_SPINS */	{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_x2 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_x3 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_x4 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_x5 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_1_CREDIT */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_2_CREDITS */	{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_4_CREDITS */	{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			},
/* ICON_10_CREDITS */	{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			}

};


////////////////////////////////////////////////////////////////////////////
/// FruitFrenzyGuts::m_aScatterWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Scatter, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_SCATTER ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_SCATTER.
////////////////////////////////////////////////////////////////////////////
FootballFrenzyGuts::SpinResult FootballFrenzyGuts::m_aScatterWinSpinResults[][6] = 
{
// Number of icons:			0			1			2						3					4					5
/* ICON_WILD */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_PIC1 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_PIC2 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_PIC3 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_PIC4 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_ACE */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_KING */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_QUEEN */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_JACK */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_TEN */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_NINE */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_SCATTER */		{	EVT_LOSER,	EVT_LOSER,	EVT_SCATTER_NEAR_MISS,	EVT_THREE_SCATTER,	EVT_FOUR_SCATTER,	EVT_FIVE_SCATTER	},
/* ICON_FREE_SPINS */	{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_x2 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_x3 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_x4 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_x5 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_1_CREDIT */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_2_CREDITS */	{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_4_CREDITS */	{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_10_CREDITS */	{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER			}
};


// Feature type, if any
int FootballFrenzyGuts::g_nReelFeatureType = -1;

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::FootballFrenzyGuts()   
///
/// This is the default constructor for the class.
////////////////////////////////////////////////////////////////////////////
FootballFrenzyGuts::FootballFrenzyGuts()
{
	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aLineWinSpinResults array. 
	C_ASSERT( countof( m_aLineWinSpinResults ) == NUM_VALID_ICONS );

	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aScatterWinSpinResults array. 
	C_ASSERT( countof( m_aScatterWinSpinResults ) == NUM_VALID_ICONS );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::~FootballFrenzyGuts()
///
/// Destructor for FootballFrenzyGuts. 
////////////////////////////////////////////////////////////////////////////
FootballFrenzyGuts::~FootballFrenzyGuts()
{
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::getRowCount()
///
/// Football Frenzy has 5 rows, instead of 3
////////////////////////////////////////////////////////////////////////////
uint8
FootballFrenzyGuts::getRowCount()
{
    return 5;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::SpinResult FootballFrenzyGuts::getWildLineWinResult( uint8* stops ) const
///
/// @param   stops			The array of stop positions for the line.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the Wild line win payout.
////////////////////////////////////////////////////////////////////////////
FootballFrenzyGuts::SpinResult FootballFrenzyGuts::getWildLineWinResult( uint8* stops, uint8 line )
{
	// Count how many Wilds we have at the beginning of the line.
	// From 1 to 4 (5 reels). Last two reels are bonus ones
	for ( uint8 i = 0; i < 5; i++ )
    {
		// If this icon is not a Wild...
		int32 iIconID = getIcon( getGameModeId(), i, stops[i] )->iconid;
		if ( iIconID != ICON_WILD )
		{
			break;	
		}
    }

	// Use the array to look up the spin result for i Wilds.
	return lookupLineWinSpinResult( ICON_WILD, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::SpinResult FootballFrenzyGuts::getLineWinResultWithPossibleSubstitution( uint8* stops, uint8 testIcon, bool& bSubstitution ) const
///
/// @param   stops			The array of stop positions for the line.
/// @param   testIcon		The icon we are looking for, typically the first icon on the line.
/// @param   bSubstitution	An output parameter that is set if a subsitution is used.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the line win payout with a possible substitution.
////////////////////////////////////////////////////////////////////////////
FootballFrenzyGuts::SpinResult FootballFrenzyGuts::getLineWinResultWithPossibleSubstitution(
	uint8* stops, uint8 line, uint8 testIcon, bool& bSubstitution )
{
	// Initialize the out parameter
	bSubstitution = false;

	// Loop through the remaining reels until the streak is over
	// From 1 to 4 (5 reels). Last two reels are bonus ones
	for ( int i = 1; i < 5; i++ )
    {
		// See what this icon is.
		uint8 currentIcon = getIcon( getGameModeId(), i, stops[i] )->iconid;

		// If the current icon is a Wild...
		if ( currentIcon == ICON_WILD )
		{
			// then use it as a substitution symbol and continue on.
			bSubstitution = true;
			continue;
		}
		
		// Otherwise if the test icon is a Wild...
		if ( testIcon == ICON_WILD )
		{
			// then we still have a substitution.  In this case the line
			// started out with a Wild.
			bSubstitution = true;

			// We have now found our first icon that is not a Wild.  All
			// of the rest of the icons will have to match this one now for the
			// streak to continue, so change our test icon.
			testIcon = currentIcon;
			continue;
		}

		// If the current icon is not the test icon...
        if ( currentIcon != testIcon )
        {
			break;											
        }
    }

	// If we made it all the way through and the testIcon is still a Wild...
	if ( testIcon == ICON_WILD )
	{
		// then this must be a 5 Wild win, so reset the substitution flag.
		bSubstitution = false;
	}

	// Use the array to look up the spin result for i testIcons.
  	return lookupLineWinSpinResult( testIcon, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 bool FootballFrenzyGuts::doesWildLineWinPayMore( SpinResult srWild, SpinResult srOther ) const
///
/// @param   srWild			The spin result to calculate the Wild payout for.
/// @param   srOther		The spin result to calculate the other payout for.
///
/// @return	 bool			True if the Wild win is larger.
///
/// This helper method is used to see which payout is larger.
////////////////////////////////////////////////////////////////////////////
bool FootballFrenzyGuts::doesWildLineWinPayMore( SpinResult srWild, SpinResult srOther) const
{
	// Lookup the payout for the Wild line win.
	long nWildLineWinPayout = getPayout( srWild )->payoutAmount;

	// Lookup the payout for the other line win.
	long nOtherLineWinPayout = getPayout( srOther )->payoutAmount;

	// Return true if the Wild payout is bigger.
    return ( nWildLineWinPayout > nOtherLineWinPayout );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::classifyStops()   
///
/// @param   stops      The array of stop positions for the line.
/// @param   bet        The value of the line bet.
/// @param   line       The index of the line.
///
/// This helper method is used to calculate the payout on a line.
////////////////////////////////////////////////////////////////////////////
void FootballFrenzyGuts::classifyStops( SpinLine* spinline, uint8* stops, uint8 bet, uint8 line, uint8& rtnresult, uint8& multiplier)
{
	// Track the Wild result separately since it might be changed to a substitution.
	SpinResult srWildLineWinResult = getWildLineWinResult( stops, line );  

	// Now see what the payout is if we use any Wild we find as substitution symbols.
	// Start with the icon on the first reel.
	uint8 testIcon = getIcon( getGameModeId(), 0, stops[0] )->iconid;

	// Declare a local variable to track whether or not we found a substitution.
	bool bSubstitution = false;

	// See what the spin result is if we used any Wild as substitution symbols.
	SpinResult srResultWithPossibleSubstitution =
		getLineWinResultWithPossibleSubstitution( stops, line, testIcon, bSubstitution );

	// Use the largest payout between the regular Wild evaluation compared to
	// the evaluation with the possible substitution.
    if ( doesWildLineWinPayMore( srWildLineWinResult, srResultWithPossibleSubstitution ) )
	{
		// The Wilds by themselves pay out more so use the Wild result.
		rtnresult = srWildLineWinResult;
	}
	else
	{
		// Otherwise the result with the substitution (if any) paid more so use it.
		rtnresult = srResultWithPossibleSubstitution;

		// If we did actually have a substitution and a winning spin...
		if ( bSubstitution && srResultWithPossibleSubstitution != EVT_LOSER )
		{
			// then add a note.
			spinline->addNote( NOTE_WILD_SUBSTITUTION );
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn		FootballFrenzyGuts::tryForJackpot()
///
///	This function tries to win the jackpot and adds the scatter if we hit
/// the jackpot.
////////////////////////////////////////////////////////////////////////////
void FootballFrenzyGuts::tryForJackpot( SlotSpin* spin )
{
	// If we are in the first paid spin (NOT a bonus spin)...
	if ( getGameModeId() == MODE_NORMAL ) 
	{
		// then try for the Random Progressive Jackpot
		// If we hit the jackpot...
		if ( getRandomProgressiveJackpotResult( spin->getSpinLineCount() ) ||
			m_bForceRandomJackpot)
		{
			// then add a jackpot scatter to the spin.
			spin->addScatter( 0,
				getPayout( EVT_RANDOM_PROGRESSIVE_WINNER )->payoutAmount, 1,
				true, EVT_RANDOM_PROGRESSIVE_WINNER, 0 );
		}
    }

	// In third party games (API) we include the jackpot in every spin so that they can update it.
	// If no jackpot, they have not won the jackpot
	setJackpotAmount( getProgressiveValue() );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::getScatterWinResult()   
///
/// @param   spin				The current spin object.
/// @param   testIcon			The scatter icon that we are searching for.
/// @param   scatterPositions	The bit mask for the scatters positions that we find.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
/// This helper method is used to evaluate the spin for scatters.
////////////////////////////////////////////////////////////////////////////
FootballFrenzyGuts::SpinResult FootballFrenzyGuts::getScatterWinResult( SlotSpin* spin, uint8 testIcon, int32& scatterPositions ) const
{
	// Declare a local variable to keep up with the icon count.
	uint8 numScatterIcons = 0;

	// Initialize the scatterPositions.
	scatterPositions = 0;

	// Loop through each reel.
	// From 1 to 4 (5 reels). Last two reels are bonus ones
  	for( int reel = 0; reel < 5; reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 5; row++ )
		{
			// We skip reels corners
			if ( !((reel == 0) && (row == 0 || row ==4)) &&
				 !((reel == 4) && (row == 0 || row ==4)) )
			{
				// Get the current stop.
				uint8 stop = relativeWheelPosition( getGameModeId(), reel,
					spin->getStops()[ reel ], row );

				// Get the icon for the current stop.
				uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
				
				// If the icon is the one we are looking for...
				if ( currentIcon == testIcon )
				{
					// then increase the icon count
					numScatterIcons++;

					// Set the bit in the position mask for this stop.
					scatterPositions |= 1 << position;
				}	
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}

	// Use the array to look up the spin result for numScatterIcons.
	return lookupScatterWinSpinResult( testIcon, numScatterIcons );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::evaluateScatterWin()
///
/// This function determines if the current spin wins a scatter payment and
/// adds the scatter.
////////////////////////////////////////////////////////////////////////////
void FootballFrenzyGuts::evaluateScatterWin( SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions )
{
	// If we got the scatter win (or almost got it)
	if ( srScatterResult != EVT_LOSER )
	{
		// Get the base payout for the scatter.
		int32 nPayout = getPayout( srScatterResult )->payoutAmount;
		
		// Scatters payout based on the total bet, so factor in the number of bet lines.
		nPayout *= spin->getSpinLineCount();

		// Multiplier Reel feature?
		uint8 nReelMultiplier, nNoteId;
		getGameModeMultiplierAndNoteId( nReelMultiplier, nNoteId );
		nPayout *= nReelMultiplier;

		// Add the payout (with multiplier and bet per line) to the total win for this spin.
		increaseTotalWin( nPayout * getBetPerLine() );
    
		// Add a scatter to the spin.
		SlotScatter* pScatter = spin->addScatter( scatterPositions,
			nPayout * getBetPerLine(), 1, false, srScatterResult, 0 );

		if (nReelMultiplier > 1)
		{
			
			pScatter->addNote(nNoteId);
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::classifyScatters( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This is an override of the framework method.  
/// It determines if the current spin merits free spins or scatter payments.
////////////////////////////////////////////////////////////////////////////
void FootballFrenzyGuts::classifyScatters( SlotSpin* spin )
{	
	// Try for the Random Progressive Jackpot
	tryForJackpot( spin );

	// Declare a local variable to track the scatter positions.
	int32 nScatterPositions = 0;

	// See if there is a Wand scatter.
	SpinResult srResult = getScatterWinResult( spin, ICON_SCATTER, nScatterPositions );

	// Add the scatter, if any.
	evaluateScatterWin( spin, srResult, nScatterPositions );

	//  If three or more scatters, activate free spins
	if (srResult == EVT_THREE_SCATTER || srResult == EVT_FOUR_SCATTER || srResult == EVT_FIVE_SCATTER)
	{
		initializeSubGame(1);	// First set of downs
	}
	else
	{
		m_subGame->update( SGIDX_BONUS_GAME_STATE, ffNone );  
	}

	// Add scatter for Credit feature, if any. ONLY FOR credit feature
	// Only if we have a multiplier feature
	uint8 srCreditPayoutType = 0;
	switch(g_nReelFeatureType)
	{
	case ICON_1_CREDIT :
		srCreditPayoutType = EVT_FEATURE_CREDIT_1;
		break;
	case ICON_2_CREDITS :
		srCreditPayoutType = EVT_FEATURE_CREDIT_2;	
		break;
	case ICON_4_CREDITS :
		srCreditPayoutType = EVT_FEATURE_CREDIT_4;
		break;
	case ICON_10_CREDITS :
		srCreditPayoutType = EVT_FEATURE_CREDIT_10;
		break;
	}

	if (srCreditPayoutType > 0)
	{
		int32 nCreditFeaturePayout = getPayout( srCreditPayoutType )->payoutAmount;
		
		nCreditFeaturePayout *= spin->getSpinLineCount();

		// Add the payout (with multiplier and bet per line) to the total win for this spin.
		increaseTotalWin( nCreditFeaturePayout * getBetPerLine() );
    
		// Add a scatter to the spin.
		spin->addScatter( 1, nCreditFeaturePayout * getBetPerLine(), 1, false, srCreditPayoutType, 0 );
	}
	
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::isValidGuts()   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to see if the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
bool8 FootballFrenzyGuts::isValidGuts()
{
	// Make sure that the m_aLineWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	// Note:  we can't do this in the constructor because we have to call
	// getReelCount.
	//ASSERT( countof( m_aLineWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Make sure that the m_aScatterWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	//ASSERT( countof( m_aScatterWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Now make sure that both game modes have the same number of reels.
	//ASSERT( getReelCount( MODE_NORMAL ) == getReelCount( MODE_FREE_SPIN ) );

	// Finally just check to see if the gut is within the right range. GENESIS GAMES: [9000,9500)
    return ( getGutsId() >= 9000 && getGutsId() < 9500 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
///
/// @param   nGameModeMultiplier	The multiplier for the game mode.
/// @param   nNoteId				The note ID for the game mode.
///
/// This helper method is used to get the game mode multiplier and note ID.
////////////////////////////////////////////////////////////////////////////
void FootballFrenzyGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier,
	uint8& nNoteId )
{
	// Only if we have a multiplier feature
	switch(g_nReelFeatureType)
	{
	case ICON_x2 :
		nGameModeMultiplier = 2;
		nNoteId = NOTE_FEATURE_MULTIPLIER_2;
		break;
	case ICON_x3 :
		nGameModeMultiplier = 3;
		nNoteId = NOTE_FEATURE_MULTIPLIER_3;
		break;
	case ICON_x4 :
		nGameModeMultiplier = 4;
		nNoteId = NOTE_FEATURE_MULTIPLIER_4;
		break;
	case ICON_x5 :
		nGameModeMultiplier = 5;
		nNoteId = NOTE_FEATURE_MULTIPLIER_5;
		break;
	default :
		// just defer to the base class.
		SlotGuts::getGameModeMultiplierAndNoteId( nGameModeMultiplier, nNoteId );   
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::preSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden when running in debug spins mode to alter the reel stops.
////////////////////////////////////////////////////////////////////////////
void FootballFrenzyGuts::preSpinClassifyResults( SlotSpin* spin )
{
	g_nReelFeatureType = -1;
	
	if (m_bDebugSpinsMode)
	{
		if (NextDebugStopIsFeature())
		{
			SlotGuts::preSpinClassifyResults(spin);	
		}
		else
		{
			SlotGuts::preSpinClassifyResults(spin);	

			if (NextDebugStopIsFeature())
			{
				// If next row is a bonus feature, apply it to this same spin
				processDebugStops( spin );
			}
		}
	}

	// Evaluate feature (reels 6 and 7)
	evaluateFeatureReels(spin);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::postSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden to see if we can complete the game.
////////////////////////////////////////////////////////////////////////////
void FootballFrenzyGuts::postSpinClassifyResults( SlotSpin* spin )
{
	// If we have not reached the feature cap yet and we have feature rounds...
	// NO FEATURE BONUS IN WITCHES AND WIZARDS
	/*
	if ( !getMaxPayoutAwarded() && getNumFeatureRounds() ) 
	{
		// then get ready to play the first feature round.
		initializeTotalPointsForRound();
		initializeCurrentShotsRemaining();
		initializeCurrentPoints();

		// The game can't complete until the bonus game is done.
		setGameCanComplete( false );
		setBonusGameState( BONUS_GAME_ACTIVE );
	}
	*/
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::hasSavedGames()
///
/// @param   bool8   True/False indicating if this machine supports saved gamees.
///
/// This is a framework override to determine if this machine supports saved games.
////////////////////////////////////////////////////////////////////////////
bool8 FootballFrenzyGuts::hasSavedGames()
{
	// We support saved games so always return true.
	return true;
}


/*
DEBUG FUNCTIONS
*/


////////////////////////////////////////////////////////////////////////////
/// @fn 	 FootballFrenzyGuts::Debug_SetLoserStops( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// Base class override, returns a losing spin triggered by the debug tool
////////////////////////////////////////////////////////////////////////////
void FootballFrenzyGuts::Debug_SetLoserStops( uint8* pStops )
{
	pStops[0]=0;
	pStops[1]=48;
	pStops[2]=12;
	pStops[3]=0;
	pStops[4]=8;
}

int32 FootballFrenzyGuts::getProgressiveValue()
{
   SlotsSQL *slotTracker = (SlotsSQL *) tracker;
   int32 jackpot = 0;

    if ( tracker )
    {
    	slotTracker->lock();
		slotTracker->getJackpot(getJackpotId(), jackpot, getInfo()->forMoney, 1);
    	slotTracker->unlock();
    }

	return jackpot;
}

void FootballFrenzyGuts::initDebugFunctionMap()
{
	SlotGuts::initDebugFunctionMap();

	addDebugStopFunction( EVT_DEBUG_FUNCTION_FF_FREE_SPINS, 
		new DebugStopFunc<FootballFrenzyGuts>(this, &FootballFrenzyGuts::DebugFreeSpin_FF_FreeSpins) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_FF_MULTIPLIER_2, 
		new DebugStopFunc<FootballFrenzyGuts>(this, &FootballFrenzyGuts::DebugFreeSpin_FF_Multiplier_2) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_FF_MULTIPLIER_3, 
		new DebugStopFunc<FootballFrenzyGuts>(this, &FootballFrenzyGuts::DebugFreeSpin_FF_Multiplier_3) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_FF_MULTIPLIER_4, 
		new DebugStopFunc<FootballFrenzyGuts>(this, &FootballFrenzyGuts::DebugFreeSpin_FF_Multiplier_4) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_FF_MULTIPLIER_5, 
		new DebugStopFunc<FootballFrenzyGuts>(this, &FootballFrenzyGuts::DebugFreeSpin_FF_Multiplier_5) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_FF_CREDITS_1, 
		new DebugStopFunc<FootballFrenzyGuts>(this, &FootballFrenzyGuts::DebugFreeSpin_FF_Credits_1) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_FF_CREDITS_2, 
		new DebugStopFunc<FootballFrenzyGuts>(this, &FootballFrenzyGuts::DebugFreeSpin_FF_Credits_2) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_FF_CREDITS_4, 
		new DebugStopFunc<FootballFrenzyGuts>(this, &FootballFrenzyGuts::DebugFreeSpin_FF_Credits_4) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_FF_CREDITS_10, 
		new DebugStopFunc<FootballFrenzyGuts>(this, &FootballFrenzyGuts::DebugFreeSpin_FF_Credits_10) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_FF_NO_FEATURE, 
		new DebugStopFunc<FootballFrenzyGuts>(this, &FootballFrenzyGuts::DebugFreeSpin_FF_No_Feature) ); 
}

void FootballFrenzyGuts::DebugFreeSpin_FF_FreeSpins( SlotSpin *spin )
{
	uint8* pStops = const_cast<uint8*>(spin->getStops());	
	
	// Free Spins, two free spins icons in middle row
	pStops[5] = 13;
	pStops[6] = 13;
}

void FootballFrenzyGuts::DebugFreeSpin_FF_Multiplier_2( SlotSpin *spin )
{
	uint8* pStops = const_cast<uint8*>(spin->getStops());	
	
	// Multiplier x2, two multiplierx2 in middle row
	pStops[5] = 6;
	pStops[6] = 6;
}

void FootballFrenzyGuts::DebugFreeSpin_FF_Multiplier_3( SlotSpin *spin )
{
	uint8* pStops = const_cast<uint8*>(spin->getStops());

	// Multiplier x3, two multiplierx3 in middle row
	pStops[5] = 12;
	pStops[6] = 12;
}

void FootballFrenzyGuts::DebugFreeSpin_FF_Multiplier_4( SlotSpin *spin )
{
	uint8* pStops = const_cast<uint8*>(spin->getStops());

	// Multiplier x4, two multiplierx4 in middle row
	pStops[5] = 15;
	pStops[6] = 15;
}

void FootballFrenzyGuts::DebugFreeSpin_FF_Multiplier_5( SlotSpin *spin )
{
	uint8* pStops = const_cast<uint8*>(spin->getStops());

	// Multiplier x5, two multiplierx5 in middle row
	pStops[5] = 27;
	pStops[6] = 28;
}

void FootballFrenzyGuts::DebugFreeSpin_FF_Credits_1( SlotSpin *spin )
{
	uint8* pStops = const_cast<uint8*>(spin->getStops());

	// 1 Credit, two 1_Credit in middle row
	pStops[5] = 1;
	pStops[6] = 1;
}

void FootballFrenzyGuts::DebugFreeSpin_FF_Credits_2( SlotSpin *spin )
{
	uint8* pStops = const_cast<uint8*>(spin->getStops());

	// 2 Credit, two 2_Credit in middle row
	pStops[5] = 5;
	pStops[6] = 5;
}

void FootballFrenzyGuts::DebugFreeSpin_FF_Credits_4( SlotSpin *spin )
{
	uint8* pStops = const_cast<uint8*>(spin->getStops());

	// 4 Credit, two 4_Credit in middle row
	pStops[5] = 7;
	pStops[6] = 7;
}

void FootballFrenzyGuts::DebugFreeSpin_FF_Credits_10( SlotSpin *spin )
{
	uint8* pStops = const_cast<uint8*>(spin->getStops());

	// 10 Credit, two 10_Credit in middle row
	pStops[5] = 3;
	pStops[6] = 3;
}  

void FootballFrenzyGuts::DebugFreeSpin_FF_No_Feature( SlotSpin *spin )
{
	uint8* pStops = const_cast<uint8*>(spin->getStops());

	// No feature in middle row
	pStops[5] = 0;
	pStops[6] = 1;
}  

bool8 FootballFrenzyGuts::NextDebugStopIsFeature()
{
	if ( m_nDebugSpinIndex >= m_debugReelStops.size() )
    {
		return false;
    }
	return ( m_debugReelStops[m_nDebugSpinIndex][1] >= EVT_DEBUG_FUNCTION_FF_FREE_SPINS );	
}

void FootballFrenzyGuts::evaluateFeatureReels(SlotSpin* spin)
{
	// Check feature game: if two stops of last two reels (6 and 7) are the same, trigger feature
	// See what this icon is.
	uint8 stopReel6 = relativeWheelPosition( getGameModeId(), 5, spin->getStops()[ 5 ], 2 );
	uint8 stopReel7 = relativeWheelPosition( getGameModeId(), 6, spin->getStops()[ 6 ], 2 );
	uint8 fIconReel6 = getIcon( getGameModeId(), 5, stopReel6 )->iconid;	
	uint8 fIconReel7 = getIcon( getGameModeId(), 6, stopReel7 )->iconid;	
	
	if (fIconReel6 == fIconReel7)
	{
		if (fIconReel6 == ICON_FREE_SPINS)
		{
			// Set or increment free spins for NEXT spin
			incrementFreeSpins( 5 );
			setGameModeId( MODE_FREE_SPIN );
		}
		g_nReelFeatureType = fIconReel6;
	}
}

void FootballFrenzyGuts::initializeSubGame ( uint8 SetOfDowns )
{
	uint8 nBaseYardsCurrentSet;
	int8 nYardsCurrentSet;
	int8 nMissingYards;
	int8 nCurrentYards;
	
	/*
	uint8 nCurrentBaseYards = 0;
	int8 nCurrentYards = 0;
	int8 nMissingYards = 0;
	*/
	
	// Initial yards of this set of downs
	switch(SetOfDowns)
	{
		case 1 :	m_subGame->update(SGDIDX_FF_SET_BASE_YARDS, 30);
					break;
		case 3 :	// We have to increment current yards, to do the trick and be in 60 yards
					nBaseYardsCurrentSet = m_subGame->getData(SGDIDX_FF_SET_BASE_YARDS);
					nYardsCurrentSet = m_subGame->getData(SGDIDX_FF_YARDS);		
					nCurrentYards = m_subGame->getData(SGDIDX_FF_CURRENT_YARDS);		
					nMissingYards = 60 - (nBaseYardsCurrentSet + nYardsCurrentSet);
					nCurrentYards = nCurrentYards + nMissingYards;
					m_subGame->update(SGDIDX_FF_CURRENT_YARDS, nCurrentYards);
					// In third set of downs, base yards is always cheated to 60
					m_subGame->update(SGDIDX_FF_SET_BASE_YARDS, 60);
					break;
		case 5 :	// We have to increment current yards, to do the trick and be in 90 yards
					nBaseYardsCurrentSet = m_subGame->getData(SGDIDX_FF_SET_BASE_YARDS);
					nYardsCurrentSet = m_subGame->getData(SGDIDX_FF_YARDS);		
					nCurrentYards = m_subGame->getData(SGDIDX_FF_CURRENT_YARDS);		
					nMissingYards = 90 - (nBaseYardsCurrentSet + nYardsCurrentSet);
					nCurrentYards = nCurrentYards + nMissingYards;
					m_subGame->update(SGDIDX_FF_CURRENT_YARDS, nCurrentYards);
					// In fifth set of downs, base yards is always cheated to 90
					m_subGame->update(SGDIDX_FF_SET_BASE_YARDS, 90);
					break;
		default:	nBaseYardsCurrentSet = m_subGame->getData(SGDIDX_FF_SET_BASE_YARDS);
					nYardsCurrentSet = m_subGame->getData(SGDIDX_FF_YARDS);
					nBaseYardsCurrentSet += nYardsCurrentSet;
					m_subGame->update(SGDIDX_FF_SET_BASE_YARDS, nBaseYardsCurrentSet);					
	}

	if (SetOfDowns <= 5)
	{
		m_subGame->update(SGDIDX_FF_SET_OF_DOWNS, SetOfDowns);
		m_subGame->update(SGDIDX_FF_DOWN, 1);
		m_subGame->update(SGDIDX_FF_YARDS, 0);
		m_subGame->update(SGDIDX_FF_CREDITS_WON, (SetOfDowns-1)*8 );
		
		m_subGame->update( SGIDX_BONUS_GAME_STATE, ffGame );  
	}
	else
	{
		m_subGame->update(SGDIDX_FF_SET_OF_DOWNS, 0);
		m_subGame->update(SGDIDX_FF_DOWN, 0);
		m_subGame->update(SGDIDX_FF_YARDS, 0);
		m_subGame->update(SGDIDX_FF_CREDITS_WON, 32 );

		m_subGame->update( SGIDX_BONUS_GAME_STATE, ffMultiplier );  
	}

	m_subGame->setServerOnly(SGDIDX_FF_SET_OF_DOWNS, false);
	m_subGame->setServerOnly(SGDIDX_FF_DOWN, false);
	m_subGame->setServerOnly(SGDIDX_FF_YARDS, false);
	m_subGame->setServerOnly(SGDIDX_FF_CREDITS_WON, false);

	setGameCanComplete( false );
}

void FootballFrenzyGuts::endSubGame( )
{
	m_subGame->update(SGIDX_BONUS_GAME_STATE, ffNone);
	setGameCanComplete( true );
}

bool8
FootballFrenzyGuts::processClientData( uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData )
{
	// Start by assuming that we do not need to reply to this message
	bool8 bReply = false;

	if ( m_subGame->getData(SGIDX_BONUS_GAME_STATE) == ffGame )
	{
		// The message we are expecting from the client is a game of the 9 they can choose
		if (bonusDataCount == 1 && bonusData != NULL)
		{
			uint8 nSetOfDowns = m_subGame->getData(SGDIDX_FF_SET_OF_DOWNS);
			int8 nYards = m_subGame->getData(SGDIDX_FF_YARDS);
			uint8 nDown = m_subGame->getData(SGDIDX_FF_DOWN);
			int8 nCreditsWon = m_subGame->getData(SGDIDX_FF_CREDITS_WON);
			
			int8 nGameYards = getRandomValueFromProbabilityTable( nSetOfDowns - 1 );
			m_subGame->update(SGDIDX_FF_CURRENT_YARDS, nGameYards);

			if ((nYards + nGameYards) >= 10)
			{
				// 10 yards, so next set of downs
				nYards += nGameYards;
				m_subGame->update(SGDIDX_FF_YARDS, nYards);
				initializeSubGame ( nSetOfDowns + 1 );
			}
			else if (nDown == 4)
			{
				// This was the last down, so feature is over
				nYards += nGameYards;
				nYards = nYards > 0 ? nYards : 0;
				nCreditsWon = ((nSetOfDowns-1) * 8) + nYards;
				m_subGame->update(SGDIDX_FF_CREDITS_WON, nCreditsWon);

				// Add bonus to game result
				int32 nBetPerLine = m_subGame->getData(SGIDX_BET_PER_LINE);
				int32 nLinesBet = m_subGame->getData(SGIDX_NUM_LINES_BET);
				int32 nBonusAmount = nCreditsWon * nBetPerLine * nLinesBet;
				addBonusGame(0, 0, nBonusAmount, 1, nBonusAmount, 0); 

				endSubGame();
			}
			else
			{
				// Next down
				nDown++;
				nYards += nGameYards;
				nCreditsWon = ((nSetOfDowns-1) * 8) + nYards;

				m_subGame->update(SGDIDX_FF_YARDS, nYards);
				m_subGame->update(SGDIDX_FF_DOWN, nDown);
				//m_subGame->update(SGDIDX_FF_CREDITS_WON, nCreditsWon);
				// We don't update credits won here, for Genesis request
			}
		}

		bReply = true;
	}
	else if ( m_subGame->getData(SGIDX_BONUS_GAME_STATE) == ffMultiplier )
	{
		// The message we are expecting from the client is a window of the 3 they can choose
		if (bonusDataCount == 1 && bonusData != NULL)
		{
			int8 nMultiplier = getRandomValueFromProbabilityTable( PT_MULTIPLIER );
			
			int32 nBetPerLine = m_subGame->getData(SGIDX_BET_PER_LINE);
			int32 nLinesBet = m_subGame->getData(SGIDX_NUM_LINES_BET);
			
			int32 nBonusAmount = 32 * nBetPerLine * nLinesBet * nMultiplier;
			addBonusGame(1, 0, nBonusAmount, nMultiplier, nBonusAmount, 0);
			
			endSubGame();
		}

		bReply = true;
	}

	return bReply;
}

SlotPaylinePosition *
FootballFrenzyGuts::getPayline(uint8 paylineid, uint8 reelid)
{
    SlotPaylinePosition *position = (SlotPaylinePosition *) 0;

    if ( reelid < m_paylines.size() )
    {
        SlotPaylineReel *paylinereel = m_paylines[reelid];

        if ( paylineid <= paylinereel->size() )
        {
            /**
             * Payline id is 1 based.
             */
            position = (*paylinereel)[paylineid - 1];
        }
        else
        {
            /**
             * paylineid is invalid.
             */
            ASSERT(0);
        }
    }
	else
	{
		// If feature reel (not included in payline) we don't care about the value, so, empty value
		SlotPaylineReel *paylinereel = m_paylines[0];
		position = (*paylinereel)[0];
	}
    //else
    //{
        /**
         * reelid is invalid.
         */
    //    ASSERT(0);
    //}

    return position;
}
